package sm.hibernate.type;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.mina.core.buffer.IoBuffer;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.usertype.CompositeUserType;

import sm.codec.api.Codec;
import sm.codec.api.CodecFactory;
import sm.codec.exception.DecodeException;
import sm.codec.exception.EncodeException;
import sm.codec.type.NullValue;
import sm.codec.type.RC;
import sm.util.Util;

public abstract class AbstractRCCompositeType implements CompositeUserType {

	private Codec codec = CodecFactory.RC_CODEC;

	private static final int MAX_SIZE = 65536;

	@Override
	public String[] getPropertyNames() {
		return new String[] {};
	}

	@Override
	public org.hibernate.type.Type[] getPropertyTypes() {
		return new org.hibernate.type.Type[] { Hibernate.BLOB };
	}

	@Override
	public Object getPropertyValue(Object component, int property)
			throws HibernateException {
		return null;
	}

	@Override
	public void setPropertyValue(Object component, int property, Object value)
			throws HibernateException {
	}

	@Override
	public boolean equals(Object x, Object y) throws HibernateException {
		return Util.equals(x, y);
	}

	@Override
	public int hashCode(Object x) throws HibernateException {
		return x == null ? 0 : x.hashCode();
	}

	@Override
	public boolean isMutable() {
		return true;
	}

	@Override
	public Serializable disassemble(Object value, SessionImplementor session)
			throws HibernateException {
		return (Serializable) deepCopy(value);
	}

	@Override
	public Object assemble(Serializable cached, SessionImplementor session,
			Object owner) throws HibernateException {
		return deepCopy(cached);
	}

	@Override
	public Object replace(Object original, Object target,
			SessionImplementor session, Object owner) throws HibernateException {
		return deepCopy(original);
	}

	@Override
	public Object nullSafeGet(ResultSet rs, String[] names,
			SessionImplementor session, Object owner)
			throws HibernateException, SQLException {
		byte[] bytes = (byte[]) Hibernate.BINARY.nullSafeGet(rs, names,
				session, owner);
		Object value = null;
		if (bytes != null && bytes.length > 0) {
			IoBuffer buffer = IoBuffer.wrap(bytes);
			RC rc = null;
			try {
				rc = (RC) codec.decode(buffer);
			} catch (DecodeException e) {
				throw new HibernateException(e.getMessage(), e);
			}

			value = rcToValue(rc, owner);
		}
		return value;
	}

	protected abstract Object rcToValue(RC rc, Object owner);

	protected abstract RC valueToRC(Object value);

	@Override
	public void nullSafeSet(PreparedStatement st, Object value, int index,
			SessionImplementor session) throws HibernateException, SQLException {
		byte[] bytes = null;
		if (value != null && !(value instanceof NullValue)) {
			RC rc = valueToRC(value);
			if (rc != null) {
				IoBuffer buffer = IoBuffer.allocate(MAX_SIZE);
				buffer.setAutoExpand(true);
				try {
					codec.encode(buffer, rc);
				} catch (EncodeException e) {
					throw new HibernateException(e.getMessage(), e);
				}
				buffer.flip();
				bytes = new byte[buffer.limit()];
				buffer.get(bytes);
			}
		}
		Hibernate.BINARY.nullSafeSet(st, bytes, index, session);
	}

}
